package com.innjia.utils;

import com.innjia.base.utils.DateConverUtil;
import com.innjia.base.utils.ValidatorUtils;
import com.innjia.sys.entity.HouseEntity;
import com.innjia.sys.service.HouseService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;

import java.util.Date;

@Component
public class FlowNumUtil {

	@Autowired
	private HouseService houseService;

	/**
	 * 获取流水号
	 * 
	 * @param key
	 * @return
	 */
	public String getFlowNum(String key) throws Exception {
		Jedis jedis = null;
		String flowNum = null;
		String nowDay = DateConverUtil.getSbyDT(new Date(), DateConverUtil.TimeType.type411.getValue());
		// 此处nowDay的格式:20170906
		String newkey = key + "_" + nowDay;
		try {
			jedis = RedisUtil.getJedis();
			flowNum = jedis.get(newkey);
			if (StringUtils.isBlank(flowNum)) {
				// TODO查询数据库
				Boolean flag = LockKeyUtil.getLockKey(key);
				if (flag) {
					// 获取锁成功，可以从数据库获取最新的数据，并且为redis重新设值
					HouseEntity house = houseService.getLatestHouse();
					if (house == null) {
						throw new RuntimeException("未发现最新的house");
					} else {
						String serialNum = house.getSerialNum();// 从数据库中获取到的最新房源序列号
						int start = serialNum.length() - 6;
						String numStr = StringUtils.substring(serialNum,start);
						long num = Long.parseLong(numStr);//数据库中的最新值
						num = num + 1;//将要返回给调用者的值
						flowNum = formartStr(nowDay, num + "", 6);//返回给调用者的flowNum
						num = num + 1;//设置到redis中的值
						jedis.set(newkey, num + "");
						LockKeyUtil.delLockKey(key);
						return flowNum;
					}

				} else {
					// 获取锁失败，有别人在操作，多尝试几次
					for (int i = 0; i < 10; i++) {
						Thread.sleep(500);
						flowNum = jedis.get(newkey);
						if (StringUtils.isNotBlank(flowNum)) {
							return flowNum;
						}
					}
					// 10次尝试后从redis中还获取不到值，直接走到异常逻辑
					throw new RuntimeException("多次尝试任然获取不到");
				}
			}else {
				jedis.incr(newkey);
				// 格式化并返回合适的格式
				return formartStr(nowDay, flowNum, 6);				
			}
		} catch (Exception e) {
			//出现异常，直接从数据库中查找最新的并且返回
			
			HouseEntity house = houseService.getLatestHouse();
			if (house == null) {
				throw new RuntimeException("未发现最新的house");
			} else {
				String serialNum = house.getSerialNum();// 从数据库中获取到的最新房源序列号
				int start = serialNum.length() - 6;
				String numStr = StringUtils.substring(serialNum,start);
				long num = Long.parseLong(numStr);//数据库中的最新值
				num = num + 1;//将要返回给调用者的值
				flowNum = formartStr(nowDay, num + "", 6);//返回给调用者的flowNum
				num = num + 1;//设置到redis中的值
				jedis.set(newkey, num + "");
				return flowNum;
			}
			
			
		} finally {
			RedisUtil.releaseRedis(jedis);
		}
	}


	/**
	 * 重置流水号
	 * 
	 * @param  key
	 * @return
	 */
	public void resetFlowNum(String key) throws Exception {
		Jedis jedis = null;
		String nowDay = DateConverUtil.getSbyDT(new Date(), DateConverUtil.TimeType.type411.getValue());
		try {
			jedis = RedisUtil.getJedis();
			jedis.set(key + "_" + nowDay, "1");
		} catch (Exception e) {
			jedis.set(key + "_" + nowDay, "1");
			throw e;
		} finally {
			RedisUtil.releaseRedis(jedis);
		}
	}
	
	/**
	 * 为字符串左侧添加适当位数的0，并且拼接到一个字符串后面
	 * @param preStr flowNum的前缀   例如：20171009
	 * @param numStr 后缀字符串
	 * @param n 后缀结果的总位数
	 * @return
	 */
	private String formartStr(String preStr, String numStr, int n) {
		n = n > 0 ? n : 6;//默认总位数为6
		int length = numStr.length();
		for (int i = 0; i < n - length; i++) {
			numStr = "0" + numStr;
		}
		return preStr + numStr;
	}

	/*
	 * 获得房间序列号
	 * 房源的编码组成规则：前6位：时间戳年（后两位）月（两位）日（两位），后4位：小写字母+数字随机
	 *
	 * @retryLimit 重试次数限制
	 */
	public String getHouseSerialNum(int retryLimit){
		if(retryLimit <= 0){
			retryLimit = 1;
		}

		for(int i = 0; i < retryLimit; i++){
			String dateStr = DateConverUtil.getSbyDT(new Date(), DateConverUtil.TimeType.type222.getValue());
			String randomStr = CustomRandomUtils.randomStrBothCharNum(4, 1);

			String serialNum = dateStr + randomStr;

			//查询数据库判断序列号是否重复
			if(ValidatorUtils.isNotEmpty(houseService.findHouseBySerialNum(serialNum))){
				continue;
			}

			return serialNum;
		}

		return null;
	}

}
